package data

import (
	"context"
	"kratos-layout/pkg/logger"
	"time"

	"kratos-layout/internal/conf"

	"github.com/go-kratos/kratos/v2/log"
	"github.com/google/wire"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"go.mongodb.org/mongo-driver/mongo/readpref"
	"go.mongodb.org/mongo-driver/x/mongo/driver/connstring"
)

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewGreeterRepo)

// Data .
type Data struct {
	DB      *mongo.Database
	Client  *mongo.Client
	Encrypt bool
}

// NewData .
func NewData(c *conf.Data, l log.Logger) (*Data, func(), error) {
	if c.Database.Driver != "mongo" || c.Database.Source == "" {
		return nil, func() {}, nil
	}
	conn, err := connstring.Parse(c.Database.Source)
	if err != nil {
		panic(err)
	}
	db, client, err := MongoConnect(c.Database.Source, conn.Database)
	if err != nil {
		logger.Errorf("MongoConnect error: %v", err)
		return nil, nil, err
	}
	cleanup := func() {
		client.Disconnect(context.TODO())
	}

	return &Data{DB: db, Client: client, Encrypt: c.Database.FieldEncrypt}, cleanup, nil
}

func MongoConnect(uri, dbname string) (*mongo.Database, *mongo.Client, error) {
	ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second)
	defer cancel()
	client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
	if err != nil {
		return nil, nil, err
	}
	ctxPing, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	err = client.Ping(ctxPing, readpref.Primary())
	if err != nil {
		return nil, nil, err
	}
	db := client.Database(dbname)

	return db, client, nil
}
